/**
 * @author wurelong
 */
const _eventsMap = new WeakMap();

class EventBus {
  constructor() {
    _eventsMap.set(this, {});
  }

  /**
   * 注册事件和处理函数
   * @param {string|Array} event - 事件名称或事件名称数组
   * @param {Function} fn - 处理函数
   */
  on(event, fn) {
    const events = _eventsMap.get(this);
    if (Array.isArray(event)) {
      event.forEach(e => this.on(e, fn));
    } else {
      (events[event] || (events[event] = [])).push(fn);
    }
  }

  /**
   * 注册事件和处理函数，触发一次后销毁
   * @param {string} event - 事件名称
   * @param {Function} fn - 处理函数
   */
  once(event, fn) {
    const handler = (...args) => {
      this.off(event, handler);
      fn(...args);
    };
    handler.fn = fn;
    this.on(event, handler);
  }

  /**
   * 销毁事件和处理函数
   * @param {string|Array} event - 事件名称或事件名称数组
   * @param {Function} [fn] - 处理函数
   */
  off(event, fn) {
    const events = _eventsMap.get(this);
    if (!arguments.length || event == null) {
      Object.keys(events).forEach(key => delete events[key]);
      return;
    }
    if (Array.isArray(event)) {
      event.forEach(e => this.off(e, fn));
      return;
    }
    const cbs = events[event];
    if (!cbs) return;
    if (fn == null) {
      delete events[event];
    } else {
      events[event] = cbs.filter(cb => cb !== fn && cb.fn !== fn);
    }
  }

  /**
   * 克隆数组
   * @param {Array} array - 要克隆的数组
   * @returns {Array} 克隆后的数组
   */
  cloneArray(array) {
    return Array.isArray(array) ? array.slice() : [];
  }

  /**
   * 触发某事件所有回调并带参数
   * @param {string} event - 事件名称
   * @param {...*} args - 传递给回调函数的参数
   */
  emit(event, ...args) {
    const events = _eventsMap.get(this);
    const cbs = this.cloneArray(events[event]);
    cbs.forEach(cb => {
      try {
        cb(...args);
      } catch (ex) {
        console.error(ex);
      }
    });
  }

  /**
   * 判断是否已注册了事件
   * @param {string} event - 事件名称
   * @param {Function} [fn] - 处理函数
   * @returns {boolean} 是否已注册事件
   */
  hasEvent(event, fn) {
    const events = _eventsMap.get(this);
    const cbs = events[event];
    if (!cbs) return false;
    if (arguments.length === 1) return true;
    return cbs.some(cb => cb === fn || cb.fn === fn);
  }

  /**
   * 清空所有事件
   */
  clear() {
    const events = _eventsMap.get(this);
    Object.keys(events).forEach(key => delete events[key]);
  }
}

// 导出 EventBus
export default EventBus;
